#include "DXUT.h"
#include "SoundEngine.h"

SoundEngine::SoundEngine()
{
    bFileOpened     = false;
    bInitialized    = false;
    bReleaseDS      = false;
    pDS             = NULL;
    pDSB            = NULL;
    bLoop           = false;
    bDone           = false;
    bAlmostDone     = false;
	id				= 0;
	fUpdateTime		= 0.0f;
}

SoundEngine::~SoundEngine()
{
	for (auto it = m_Sounds.begin(); it != m_Sounds.end(); ) {
		Sound sound = it->second;
		sound.Close();
		it = m_Sounds.erase(it);
	}

    if (bFileOpened)
        Close();

    if (bReleaseDS && pDS)
        pDS->Release();
}

bool SoundEngine::InitDirectSound( HWND hWnd )
{
	HRESULT hr;
	
	if (FAILED(hr = DirectSoundCreate8(NULL, &pDS, NULL)))
        return bInitialized = false;

	pDS->Initialize(NULL);
	pDS->SetCooperativeLevel( hWnd, DSSCL_PRIORITY );

    bReleaseDS = true;

    return bInitialized = true;
}

bool SoundEngine::OpenOgg( char *filename )
{
    if (!bInitialized)
        return false;

    if (bFileOpened)
        Close();

    FILE    *f;

    fopen_s(&f, filename, "rb");
    if (!f) return false;

    ov_open(f, &vf, NULL, 0);

    // ok now the tricky part

    // the vorbis_info struct keeps the most of the interesting format info
    vorbis_info *vi = ov_info(&vf,-1);

    // set the wave format
	WAVEFORMATEX	    wfm;

    memset(&wfm, 0, sizeof(wfm));

    wfm.cbSize          = sizeof(wfm);
    wfm.nChannels       = vi->channels;
    wfm.wBitsPerSample  = 16;                    // ogg vorbis is always 16 bit
    wfm.nSamplesPerSec  = vi->rate;
    wfm.nAvgBytesPerSec = wfm.nSamplesPerSec*wfm.nChannels*2;
    wfm.nBlockAlign     = 2*wfm.nChannels;
    wfm.wFormatTag      = 1;


    // set up the buffer
	DSBUFFERDESC desc;

	desc.dwSize         = sizeof(desc);
	desc.dwFlags        = 0;
	desc.lpwfxFormat    = &wfm;
	desc.dwReserved     = 0;

    desc.dwBufferBytes  = BUFSIZE*2;
    pDS->CreateSoundBuffer(&desc, &pDSB, NULL );

    // fill the buffer

    DWORD   pos = 0;
    int     sec = 0;
    int     ret = 1;
    DWORD   size = BUFSIZE*2;

    char    *buf;

    pDSB->Lock(0, size, (LPVOID*)&buf, &size, NULL, NULL, DSBLOCK_ENTIREBUFFER);
    
    // now read in the bits
    while(ret && pos<size)
    {
        ret = ov_read(&vf, buf+pos, size-pos, 0, 2, 1, &sec);
        pos += ret;
    }

	pDSB->Unlock( buf, size, NULL, NULL );

    nCurSection         =
    nLastSection        = 0;

    return bFileOpened = true;
}

void SoundEngine::Close()
{
    bFileOpened = false;

    if (pDSB)
        pDSB->Release();
}


void SoundEngine::Play(bool loop)
{
    if (!bInitialized)
        return;

    if (!bFileOpened)
        return;

    // play looping because we will fill the buffer
    pDSB->Play(0,0,DSBPLAY_LOOPING);

    bLoop = loop;
    bDone = false;
    bAlmostDone = false;
}

void SoundEngine::Stop()
{
    if (!bInitialized)
        return;

    if (!bFileOpened)
        return;

    pDSB->Stop();
}

void SoundEngine::Update()
{
    DWORD pos;

    pDSB->GetCurrentPosition(&pos, NULL);

    nCurSection = pos<BUFSIZE?0:1;

    // section changed?
    if (nCurSection != nLastSection)
    {
        if (bDone && !bLoop)
            Stop();

        // gotta use this trick 'cause otherwise there wont be played all bits
        if (bAlmostDone && !bLoop)
            bDone = true;

        DWORD   size = BUFSIZE;
        char    *buf;

        // fill the section we just left
        pDSB->Lock( nLastSection*BUFSIZE, size, (LPVOID*)&buf, &size, NULL, NULL, 0 );

        DWORD   pos = 0;
        int     sec = 0;
        int     ret = 1;
                
        while(ret && pos<size)
        {
            ret = ov_read(&vf, buf+pos, size-pos, 0, 2, 1, &sec);
            pos += ret;
        }

        // reached the and?
        if (!ret && bLoop)
        {
            // we are looping so restart from the beginning
            // NOTE: sound with sizes smaller than BUFSIZE may be cut off

            ret = 1;
            ov_pcm_seek(&vf, 0);
            while(ret && pos<size)
            {
                ret = ov_read(&vf, buf+pos, size-pos, 0, 2, 1, &sec);
                pos += ret;
            }
        }
        else if (!ret && !(bLoop))
        {
            // not looping so fill the rest with 0
         //   while(pos<size)
         //       *(buf+pos)=0; pos ++;

            // and say that after the current section no other sectin follows
            bAlmostDone = true;
			bDone		= true;
        }
                
        pDSB->Unlock( buf, size, NULL, NULL );
    
        nLastSection = nCurSection;
    }
}

void SoundEngine::OnFrameMove( double fTime, float fElapsedTime) {
	fUpdateTime += fElapsedTime;
	if (fUpdateTime >= 0.2f) {
		for (auto it = m_Sounds.begin(); it != m_Sounds.end(); ) {
			Sound sound = it->second;
			if ( sound.Update() ) {
				//sound.Update();			
				it++;
			} else {
				sound.Close();
				it = m_Sounds.erase(it);
			}
		}
		if( IsPlaying() )
			Update();
		fUpdateTime = 0.0f;
	}
}

void SoundEngine::Quickplay( char *filename ) {
	Sound sound;
	if (m_Sounds.size() < 6) {
		sound.Open(this, filename);
		sound.Play();
		m_Sounds.insert(std::pair<int,Sound>(id++, sound));
	}
}

SoundEngine::Sound::Sound() {
	pDSB			= NULL;
	nLastSection	= 0;          
	nCurSection		= 0;           
	bAlmostDone		= false;            
	bDone			= false;                  
	bLoop			= false;
}

bool SoundEngine::Sound::Update()
{
    DWORD pos;

    pDSB->GetCurrentPosition(&pos, NULL);

    nCurSection = pos<BUFSIZE?0:1;

    // section changed?
    if (nCurSection != nLastSection)
    {

        if (bDone && !bLoop) {
            Stop();
		}

        // gotta use this trick 'cause otherwise there wont be played all bits
        if (bAlmostDone && !bLoop)
            bDone = true;

        DWORD   size = BUFSIZE;
        char    *buf;

        // fill the section we just left
        pDSB->Lock( nLastSection*BUFSIZE, size, (LPVOID*)&buf, &size, NULL, NULL, 0 );

        DWORD   pos = 0;
        int     sec = 0;
        int     ret = 1;
                
        while(ret && pos<size)
        {
            ret = ov_read(&vf, buf+pos, size-pos, 0, 2, 1, &sec);
            pos += ret;
        }

        // reached the and?
        if (!ret && bLoop)
        {
            // we are looping so restart from the beginning
            // NOTE: sound with sizes smaller than BUFSIZE may be cut off

            ret = 1;
            ov_pcm_seek(&vf, 0);
            while(ret && pos<size)
            {
                ret = ov_read(&vf, buf+pos, size-pos, 0, 2, 1, &sec);
                pos += ret;
            }
        }
        else if (!ret && !(bLoop))
        {
            // not looping so fill the rest with 0
         //   while(pos<size)
         //       *(buf+pos)=0; pos ++;

            // and say that after the current section no other sectin follows
            bAlmostDone = true;
			bDone		= true;
			//Stop();
        }
                
        pDSB->Unlock( buf, size, NULL, NULL );

   
        nLastSection = nCurSection;
    }
	return !bDone;
}

void SoundEngine::Sound::Close() {
    if (pDSB)
        pDSB->Release();
}

void SoundEngine::Sound::Play(bool loop) {
    // play looping because we will fill the buffer
	pDSB->Play(0,0,DSBPLAY_LOOPING);

    bLoop		= loop;
    bDone		= false;
    bAlmostDone = false;
}

void SoundEngine::Sound::Stop() {
    pDSB->Stop();
}

void SoundEngine::Sound::Open( SoundEngine * engine, char *filename ) {

    FILE    *f;

    fopen_s(&f, filename, "rb");
    if (!f) return;

    ov_open(f, &vf, NULL, 0);

    // ok now the tricky part

    // the vorbis_info struct keeps the most of the interesting format info
    vorbis_info *vi = ov_info(&vf,-1);

    // set the wave format
	WAVEFORMATEX	    wfm;

    memset(&wfm, 0, sizeof(wfm));

    wfm.cbSize          = sizeof(wfm);
    wfm.nChannels       = vi->channels;
    wfm.wBitsPerSample  = 16;                    // ogg vorbis is always 16 bit
    wfm.nSamplesPerSec  = vi->rate;
    wfm.nAvgBytesPerSec = wfm.nSamplesPerSec*wfm.nChannels*2;
    wfm.nBlockAlign     = 2*wfm.nChannels;
    wfm.wFormatTag      = 1;


    // set up the buffer
	DSBUFFERDESC desc;

	desc.dwSize         = sizeof(desc);
	desc.dwFlags        = 0;
	desc.lpwfxFormat    = &wfm;
	desc.dwReserved     = 0;

    desc.dwBufferBytes  = BUFSIZE*2;
    engine->pDS->CreateSoundBuffer(&desc, &pDSB, NULL );

    // fill the buffer

    DWORD   pos = 0;
    int     sec = 0;
    int     ret = 1;
    DWORD   size = BUFSIZE*2;

    char    *buf;

    pDSB->Lock(0, size, (LPVOID*)&buf, &size, NULL, NULL, DSBLOCK_ENTIREBUFFER);
    
    // now read in the bits
    while(ret && pos<size)
    {
        ret = ov_read(&vf, buf+pos, size-pos, 0, 2, 1, &sec);
        pos += ret;
    }

	pDSB->Unlock( buf, size, NULL, NULL );

    nCurSection         = 0;
    nLastSection        = 0;
}